package com.hhsj.design.pullableview.testScroller;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.ScrollView;

public class RefreshScrollView extends ScrollView {

    /**
     * 1.如果listview滑出屏幕的距离为0时，上拉时不拦截处理，下拉时拦截处理，header下面的view慢慢显示出来
     * 2.如果listview滑出屏幕的距离不为0时，执行其自身的滑动
     * 3.如果正在向下显示header下面的view时，上滑view也向上隐藏，隐藏完继续listview自身的滑动
     */
    private RelativeLayout mHeader;//头部控件
    private View mRefreshView;//刷新控件
    private LinearLayout mContainer;//中间的内容
    private int mTouchSlop;//滑动触发阈值
    private int mRefreshViewHeight;//刷新控件的高度
    private int mHeaderHeight;//头部控件的高度
    private int mTargetHeight;//触发下拉刷新的目标高度
    private int mMaxHeight;
    private float mLastX, mLastY;
    private boolean mIsRefresh;//刷新状态
    private boolean mIsDrag;//下拉状态
    private ViewGroup.LayoutParams params;//头部所在视图的
    private BackRunnable mRunnable;
    private BackRefreshStateRunnable mBackRefreshStateRunnable;
    private RefreshListener mListener;//下拉刷新方法
    private static final float DAMP = 0.2f;//阻尼系数

    public interface RefreshListener {
        void start();
    }

    public RefreshScrollView(Context context) {
        this(context, null);
    }

    public RefreshScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RefreshScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        int count = getChildCount();

        if (count != 1 || !(getChildAt(0) instanceof LinearLayout)) {
            throw new IllegalArgumentException("scrollview must be one LinearLayout");
        }
        mContainer = (LinearLayout) getChildAt(0);
    }

    public void setRefreshListener(RefreshListener listener) {
        mListener = listener;
    }

    public void initHeader(View header, View refreshHeader) {
        mHeader = new RelativeLayout(getContext());
        if (refreshHeader == null) {
            LinearLayout linearLayout = new LinearLayout(getContext());
            linearLayout.setOrientation(LinearLayout.VERTICAL);
            linearLayout.setGravity(Gravity.CENTER);
            linearLayout.setPadding(0, 30, 0, 10);
            linearLayout.addView(new ProgressBar(getContext()));

            mRefreshView = new ProgressBar(getContext());
        } else {
            mRefreshView = refreshHeader;
        }
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
        mHeader.addView(mRefreshView, lp);
        if (header != null) {
            lp = new RelativeLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            mHeader.addView(header, lp);
        }
        mContainer.addView(mHeader, 0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mRefreshView == null || mHeader == null || getScrollY() > 0) {
            return super.onInterceptTouchEvent(ev);
        }
        int action = ev.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = ev.getX();
                mLastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (isValidDrag(ev)) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //需要处理header view里面的显示的话，要设置param，否则给父类自己来处理
        if (mRefreshView == null || mHeader == null || getScrollY() > 0) {
            return super.onTouchEvent(ev);
        }

        if (mRefreshViewHeight <= 0) {
            mHeaderHeight = mHeader.getMeasuredHeight();
            mRefreshViewHeight = mRefreshView.getMeasuredHeight();
            mTargetHeight = mHeaderHeight + mRefreshViewHeight;
        }

        int action = ev.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = ev.getX();
                mLastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (getScrollY() > 0 || mIsRefresh) {
                    break;
                }
                if (!mIsDrag && isValidDrag(ev)) {
                    mIsDrag = true;
                }

                if (mIsDrag) {
                    if (ev.getY() - mLastY > 0 && !mIsRefresh) {
                        moveLayout(ev.getY() - mLastY);
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                mIsDrag = false;
                if (params == null) {
                    params = mHeader.getLayoutParams();
                }

                if (params.height <= mHeaderHeight) {
                    break;
                }

                mIsRefresh = params.height >= mTargetHeight;

                if (!mIsRefresh) {
                    //回弹到刷新的高度
                    completeRefresh();
                } else {

                    startRefreshHeight(params.height, mTargetHeight);

                    startRefreshAnimator();

                    if (mListener != null) {
                        mListener.start();
                    }
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 执行刷新动画
     */
    protected void startRefreshAnimator() {

    }

    /**
     * 弹到开始刷新的高度
     */
    private void startRefreshHeight(int height, int targetHeight) {
        if (mBackRefreshStateRunnable == null) {
            mBackRefreshStateRunnable = new BackRefreshStateRunnable(500, height, targetHeight);
        }
        mBackRefreshStateRunnable.start();
    }


    /**
     * 当下拉的时候，需要拦截
     */
    private boolean isValidDrag(MotionEvent ev) {
        if (ev == null) {
            return false;
        }
        return Math.abs(mLastX - ev.getX()) < Math.abs(mLastY - ev.getY())//上下操作
                && Math.abs(mLastY - ev.getY()) > mTouchSlop;//大于阈值
    }


    public void completeRefresh() {
        if (mRunnable == null) {
            mRunnable = new BackRunnable(500);
        }
        mRunnable.start();
    }

    private void moveLayout(float y) {
        params = mHeader.getLayoutParams();
        float changeHeight = mHeaderHeight + DAMP * y;
//        if (changeHeight >= mTargetHeight && params.height == mTargetHeight) {
//            return;
//        }
//        if (changeHeight > mTargetHeight) {
//            params.height = mTargetHeight;
//        } else {
//            params.height = (int) changeHeight;
//        }
        params.height = (int) changeHeight;

        mHeader.requestLayout();
    }


    private class BackRunnable implements Runnable {

        private int mDuration;
        private boolean mIsFinish;
        private long mStartTime;

        private Interpolator mInterpolator = new Interpolator() {

            @Override
            public float getInterpolation(float input) {
                return (float) Math.pow(input, 5);
            }
        };

        public BackRunnable(int duration) {
            mDuration = duration;
            mIsFinish = true;
        }

        public void start() {
            if (mIsFinish) {
                mStartTime = System.currentTimeMillis();
                mIsFinish = false;
                post(this);
            }
        }

        public void cancel() {
            this.mIsFinish = true;
        }

        @Override
        public void run() {
            if (!mIsFinish) {
                long delta = System.currentTimeMillis() - mStartTime;
                if (delta > mDuration) {
                    delta = mDuration;
                }
                params.height = (int) (mHeaderHeight +
                        ((mRefreshView.getY() + mRefreshViewHeight) - mHeaderHeight) * mInterpolator.getInterpolation(1f - delta * (1f / mDuration)));
                Log.e("HHSJHHSJ", "params.height = " + params.height);

                mHeader.requestLayout();
                if (params.height == mHeaderHeight) {
                    this.mIsFinish = true;
                    mIsRefresh = false;
                    return;
                }
                post(this);
            }
        }
    }

    private class BackRefreshStateRunnable implements Runnable {

        private int mDuration;
        private boolean mIsFinish;
        private long mStartTime;
        private int height;
        private int targetHeight;


        private Interpolator mInterpolator = input -> (float) Math.pow(input, 5);

        public BackRefreshStateRunnable(int duration, int height, int targetHeight) {
            mDuration = duration;
            mIsFinish = true;
            this.height = height;
            this.targetHeight = targetHeight;

        }

        public void start() {
            if (mIsFinish) {
                mStartTime = System.currentTimeMillis();
                mIsFinish = false;
                post(this);
            }
        }

        public void cancel() {
            this.mIsFinish = true;
        }

        @Override
        public void run() {
            if (!mIsFinish) {
                long delta = System.currentTimeMillis() - mStartTime;
                if (delta > mDuration) {
                    delta = mDuration;
                }
                params.height = (int) (targetHeight +
                        ((mRefreshView.getY() + mRefreshViewHeight) - targetHeight) * mInterpolator.getInterpolation(1f - delta * (1f / mDuration)));
                mHeader.requestLayout();
                if (params.height == targetHeight) {
                    this.mIsFinish = true;
                    mIsRefresh = false;
                    return;
                }
                post(this);
            }
        }
    }

}